/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package testes;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
import org.jgap.InvalidConfigurationException;
import org.jgap.gp.CommandGene;
import org.jgap.gp.GPProblem;
import org.jgap.gp.impl.GPConfiguration;
import org.jgap.gp.impl.GPGenotype;
import org.jgap.gp.terminal.Variable;

/**
 *
 * @author Heitor
 */
public class PGTeste extends  GPProblem {

    
    // number of variables to use (output variable is excluded)
    public static int numInputVariables;
    // the variables to use (of size numInputVariables)
    public static Variable[] variables;
    // variable name
    public static String[] variableNames;
    // index of the output variable
    public static Integer outputVariable; // default last

    // constants
    public static ArrayList<Double> constants = new ArrayList<Double>();

    // size of data
    public static int numRows;

    // the data (as Double)
    // Note: the last row is the output variable per default
    protected static Double[][] data;

    // If we have found a perfect solution.
    public static boolean foundPerfect  = false;
    
    // standard GP parameters
    public static int minInitDepth      = 2;
    public static int maxInitDepth      = 4;
    public static int populationSize    = 1000;
    public static int maxCrossoverDepth = 8;
    public static int programCreationMaxTries = 5;
    public static int numEvolutions     = 1800;
    public static boolean verboseOutput = true;
    public static int maxNodes          = 21;
    public static double functionProb = 0.9d;
    public static float reproductionProb = 0.1f; // float
    public static float mutationProb = 0.1f; // float
    public static float crossoverProb = 0.9f; // float
    public static float dynamizeArityProb = 0.08f; // float
    public static double newChromsPercent = 0.3d;
    public static int tournamentSelectorSize = 0;
    public static boolean noCommandGeneCloning = true;
    public static boolean strictProgramCreation = false;
    public static boolean useProgramCache = true;

    // lower/upper ranges for the Terminal
    public static double lowerRange     = -10.0d;
    public static double upperRange     = -10.0d;

    // Should the terminal be a whole numbers (integers as double) or not?
    public static boolean terminalWholeNumbers = true;

    public static String returnType = "DoubleClass"; // not used yet
    public static String presentation   = "";


    // list of functions (as strings)
    public static String[] functions    = {"Multiply","Divide","Add","Subtract"};

    // if the values are too small we may want to scale
    // the error
    public static double scaleError = -1.0d;

    // timing
    public static long startTime;
    public static long endTime;

    // if >= 0.0d, then stop if the fitness is below or equal
    // this value
    public static double stopCriteriaFitness = -1.0d;

    // shows the whole population (sorted by fitness)
    // in all generations. Mainly for debugging purposes.
    public static boolean showPopulation = false;

    // show similiar solutions with the same fitness
    // as the overall best program
    public static boolean showSimiliar = false;

    // 2010-02-27
    // how to sort the similiar solutions.
    // Valid options:
    //   - occurrence (descending, default)
    //   - length (asccending)
    public static String similiarSortMethod = "occurrence";

    // shows progression as generation number
    public static boolean showProgression = false;

    // show results for all generations
    public static boolean showAllGenerations = false;

    // show all the results for the fittest program
    public static boolean showResults = false;
    public static int resultPrecision = 5;

    // take a sample of the data set (if > 0.0)
    public static double samplePCT = 0.0d;

    // hits criteria: if >= 0.0d then collect and show
    // number of fitness values (errors) that is equal 
    // or below this value.
    public static double hitsCriteria = -1.0d;

    // withheld a percentage for validation of the
    // of the fittest program
    public static double validationPCT = 0.0d;
    protected static Double[][] validationSet;

    // for testing some values
    public static Double[][] testData;

    // for ModuloReplaceD: replacement for 0
    public static int modReplace = 0;

    // 2010-02-27
    // make time series based on a single sequence
    public static boolean makeTimeSeries = false;
    // 2010-02-27
    // make time series based on a single sequence
    // and adding the index of the instance
    public static boolean makeTimeSeriesWithIndex = false;

    // make a sequence of (ix, number) based on a single sequence
    // public static boolean makeIndexSeq = false;

    // 2010-02-22
    // Error method. 
    // Valid options: 
    //    totalError (default)
    //    minError
    //    meanError
    //    medianError
    //    maxError
    // 
    // Note that hitsCriteria > -1 overrides errorMethod
    // 
    public static String errorMethod = "totalError";

    // 2010-02-26: If we don't want to have any Terminals
    public static boolean noTerminals = false;

    // 2010-03-01: Penalty if the number of nodes in a program
    //             is less than minimum required.
    public static int minNodes = -1;
    //  The penalty for less nodes (may be application dependent)
    public static double minNodesPenalty = 0.0d;
    
    // 2010-03-02: Penalty if the variables are not different
    public static boolean alldifferentVariables = false;
    public static double alldifferentVariablesPenalty = 0.0d;

    
        @Override
    public GPGenotype create() throws InvalidConfigurationException {
            
             GPConfiguration conf = getGPConfiguration();
             // At first, we define the return type of the GP program.
        // ------------------------------------------------------
        // Then, we define the arguments of the GP parts. Normally, only for ADF's
        // there is a specification here, otherwise it is empty as in first case.
        // -----------------------------------------------------------------------
        Class[] types;
        Class[][] argTypes;
//        if (useADF) {
//            if ("boolean".equals(adfType)) {
//                types = new Class[]{CommandGene.DoubleClass, CommandGene.BooleanClass};
//            } else if ("integer".equals(adfType)) {
//                types = new Class[]{CommandGene.DoubleClass, CommandGene.IntegerClass};
//            } else {
//                types = new Class[]{CommandGene.DoubleClass, CommandGene.DoubleClass};
//            }
//
//            Class[] adfs = new Class[adfArity];
//            for (int i = 0; i < adfArity; i++) {
//                if ("boolean".equals(adfType)) {
//                    adfs[i] = CommandGene.BooleanClass;
//                } else if ("integer".equals(adfType)) {
//                    adfs[i] = CommandGene.IntegerClass;
//                } else {
//                    adfs[i] = CommandGene.DoubleClass;
//                }
//            }
//
//            argTypes = new Class[][]{{}, adfs};
//                
//        } else {
            types = new Class[]{CommandGene.DoubleClass};
            argTypes = new Class[][]{{}};
//        }
            
            
            
        // Configure desired minimum number of nodes per sub program.
        // Same as with types: First entry here corresponds with first entry in
        // nodeSets.
        // Configure desired maximum number of nodes per sub program.
        // First entry here corresponds with first entry in nodeSets.
        //
        // This is experimental!
        int[] minDepths;
        int[] maxDepths;

           
        // Next, we define the set of available GP commands and terminals to use.
        // Please see package org.jgap.gp.function and org.jgap.gp.terminal
        // You can easily add commands and terminals of your own.
        // ----------------------------------------------------------------------
        CommandGene[] commands = makeCommands(conf, functions, lowerRange, upperRange, "plain");

        // Create the node sets
        int command_len = commands.length;        
        CommandGene[][] nodeSets = new CommandGene[2][numInputVariables+command_len];
 
                // the variables:
        //  1) in the nodeSets matrix
        //  2) as variables (to be used for fitness checking)
        // --------------------------------------------------
        variables = new Variable[numInputVariables];
        int variableIndex = 0;
        for(int i = 0; i < numInputVariables+1; i++) {
            String variableName = variableNames[i];
            if (i != outputVariable) {
                if (variableNames != null && variableNames.length > 0) {
                    variableName = variableNames[i];
                }
                variables[variableIndex] = Variable.create(conf, variableName, CommandGene.DoubleClass);
                nodeSets[0][variableIndex] = variables[variableIndex];
                System.out.println("input variable: " + variables[variableIndex]);
                variableIndex++;
            }
        }
        
        // assign the functions/terminals
        // ------------------------------
        for(int i = 0; i < command_len; i++) {
            System.out.println("function1: " + commands[i]);
            nodeSets[0][i+numInputVariables] = commands[i];
        }    
        
                // Create genotype with initial population. Here, we use the 
        // declarations made above:
        // ----------------------------------------------------------
        return GPGenotype.randomInitialGenotype(conf, types, argTypes, nodeSets,
                                                maxNodes,verboseOutput);

            

    }
        
    public static void readFile(String file) {
        
        try {

            BufferedReader inr = new BufferedReader(new FileReader(file));
            String str;
            int lineCount = 0;
            boolean gotData = false;
            ArrayList<Double[]> theData = new ArrayList<Double[]>();
            ArrayList<Double[]> theValidationSet = new ArrayList<Double[]>();
            // contains user defined testdata
            ArrayList<Double[]> theTestData = new ArrayList<Double[]>();

            //
            // read the lines
            // 
            while ((str = inr.readLine()) != null) {
                lineCount++;
                str = str.trim();

                // ignore empty lines or comments, i.e. lines starting with either # or %
                // ----------------------------------------------------------------------
                if(str.startsWith("#") || str.startsWith("%") || str.length() == 0) {
                    continue;
                }

                if ("data".equals(str)) {
                    gotData = true;
                    continue;
                }

                if (gotData) {
                    // Read the data rows
                    // ------------------
                    String[] dataRowStr = str.split("[\\s,]+");
                    int len = dataRowStr.length;                   
                    Double[] dataRow = new Double[len];
                    boolean isTestData = false;
                    for (int i = 0; i < len; i++) {
                        if ("?".equals(dataRowStr[i])) {
                            isTestData = true;
                            dataRow[i] = -1.0d; // dummy value
                        } else {
                            dataRow[i] = Double.parseDouble(dataRowStr[i]);
                        }
                    }


                    // check if this row should be in the data
                    // or maybe in the validation set.
                    // A data point may not be in the both data
                    // set and validation set.
                    boolean inData = true;

                    if (isTestData) {
                        inData = false;
                        theTestData.add(dataRow);
                    }

                    // Get sample to use
                    // Keep all the rows where nextFloat is <= samplePCT
                    if (!isTestData && samplePCT > 0.0d) {
                        Random randomGenerator = new Random();
                        float rand = randomGenerator.nextFloat();
                        if (rand > samplePCT) {
                            inData = false;
                        }
                    }

                    // make validation set
                    if (!isTestData && validationPCT > 0.0d) {
                        Random randomGenerator = new Random();
                        float rand = randomGenerator.nextFloat();
                        if (rand < validationPCT) {
                            inData = false;
                            theValidationSet.add(dataRow);
                        }
                    }

                    // put in the data set
                    if (inData) {
                        theData.add(dataRow);
                    }

                } else {

                    // Check for parameters on the form
                    //    parameter: value(s)
                    // --------------------------------
                    if(str.contains(":")) {
                        String row[] = str.split(":\\s*");

                        // Now check each parameter
                        if ("return_type".equals(row[0])) {
                            returnType = row[1];

                        } else if ("presentation".equals(row[0])) {
                            presentation = row[1];
                            
                        } else if ("num_input_variables".equals(row[0])) {
                            numInputVariables = Integer.parseInt(row[1]);
                            
                        } else if ("num_rows".equals(row[0])) {
                            System.out.println("num_rows is not used anymore; it is calculated by the program.");
                            // numRows = Integer.parseInt(row[1]);
                            
                        } else if ("terminal_range".equals(row[0])) { 
                            String[] ranges = row[1].split("\\s+");
                            lowerRange = Double.parseDouble(ranges[0]); 
                            upperRange = Double.parseDouble(ranges[1]); 
                            
                        } else if ("terminal_wholenumbers".equals(row[0])) {
                          terminalWholeNumbers = Boolean.parseBoolean(row[1]);
                            
                        } else if ("max_init_depth".equals(row[0])) {
                            maxInitDepth = Integer.parseInt(row[1]);

                        } else if ("min_init_depth".equals(row[0])) {
                            minInitDepth = Integer.parseInt(row[1]);

                        } else if ("program_creation_max_tries".equals(row[0])) {
                            programCreationMaxTries = Integer.parseInt(row[1]);
                            
                        } else if ("population_size".equals(row[0])) {
                            populationSize = Integer.parseInt(row[1]); 
                            
                        } else if ("max_crossover_depth".equals(row[0])) {
                            maxCrossoverDepth = Integer.parseInt(row[1]);

                        } else if ("function_prob".equals(row[0])) {
                            functionProb = Double.parseDouble(row[1]);

                        } else if ("reproduction_prob".equals(row[0])) {
                            reproductionProb = Float.parseFloat(row[1]);

                        } else if ("mutation_prob".equals(row[0])) {
                            mutationProb = Float.parseFloat(row[1]);

                        } else if ("crossover_prob".equals(row[0])) {
                            crossoverProb = Float.parseFloat(row[1]);

                        } else if ("dynamize_arity_prob".equals(row[0])) {
                            dynamizeArityProb = Float.parseFloat(row[1]);

                        } else if ("new_chroms_percent".equals(row[0])) {
                            newChromsPercent = Double.parseDouble(row[1]);

                        } else if ("num_evolutions".equals(row[0])) {
                            numEvolutions = Integer.parseInt(row[1]); 
                            
                        } else if ("max_nodes".equals(row[0])) {
                            maxNodes = Integer.parseInt(row[1]);
                            
                        } else if ("functions".equals(row[0])) {
                            functions = row[1].split("[\\s,]+");
                            
                        } else if ("variable_names".equals(row[0])) {
                            variableNames = row[1].split("[\\s,]+");

                        } else if ("output_variable".equals(row[0])) {
                            outputVariable = Integer.parseInt(row[1]);

                    

                        } else if ("constant".equals(row[0])) {
                            Double constant = Double.parseDouble(row[1]);
                            constants.add(constant);

                        } else if ("tournament_selector_size".equals(row[0])) {
                            tournamentSelectorSize = Integer.parseInt(row[1]);

                        } else if ("scale_error".equals(row[0])) {
                            scaleError = Double.parseDouble(row[1]);

                        } else if ("stop_criteria_fitness".equals(row[0])) {
                            stopCriteriaFitness = Double.parseDouble(row[1]);

                        } else if ("show_population".equals(row[0])) {
                            showPopulation = Boolean.parseBoolean(row[1]);

                        } else if ("show_similiar".equals(row[0]) || 
                                   "show_similar".equals(row[0])) {
                            // 2010-02-27:added alternative spelling
                            showSimiliar = Boolean.parseBoolean(row[1]);

                        } else if ("similiar_sort_method".equals(row[0]) || 
                                   "similar_sort_method".equals(row[0])) {
                            // 2010-02-27
                            similiarSortMethod = row[1];

                            if (! (
                                   "length".equals(similiarSortMethod) ||
                                   "occurrence".equals(similiarSortMethod))
                                ) {
                                System.out.println("Unknown similiar_sort_method: " + similiarSortMethod);
                                System.exit(1);
                            }


                        } else if ("show_progression".equals(row[0])) {
                            showProgression = Boolean.parseBoolean(row[1]);

                        } else if ("sample_pct".equals(row[0])) {
                            samplePCT = Float.parseFloat(row[1]);

                        } else if ("validation_pct".equals(row[0])) {
                            validationPCT = Float.parseFloat(row[1]);

                        } else if ("hits_criteria".equals(row[0])) {
                            hitsCriteria = Double.parseDouble(row[1]);
                            errorMethod = "hitsCriteria";

                        } else if ("show_all_generations".equals(row[0])) {
                            showAllGenerations = Boolean.parseBoolean(row[1]);

                        } else if ("strict_program_creation".equals(row[0])) {
                            strictProgramCreation = Boolean.parseBoolean(row[1]);

                        } else if ("no_command_gene_cloning".equals(row[0])) {
                            noCommandGeneCloning = Boolean.parseBoolean(row[1]);

                        } else if ("use_program_cache".equals(row[0])) {
                            useProgramCache = Boolean.parseBoolean(row[1]);

                        } else if ("mod_replace".equals(row[0])) {
                            // this is quite experimental
                            modReplace = Integer.parseInt(row[1]);

                        } else if ("show_results".equals(row[0])) {
                            showResults = Boolean.parseBoolean(row[1]);

                        } else if ("result_precision".equals(row[0])) {
                            resultPrecision = Integer.parseInt(row[1]);

                        } else if ("error_method".equals(row[0])) {
                            errorMethod = row[1];

                            if (! (
                                   "maxError".equals(errorMethod) ||
                                   "minError".equals(errorMethod) ||
                                   "medianError".equals(errorMethod) ||
                                   "meanError".equals(errorMethod) ||
                                   "totalError".equals(errorMethod))) {
                                System.out.println("Unknown errorMethod: " + errorMethod);
                                System.exit(1);
                            }

                        } else if ("no_terminals".equals(row[0])) {
                            // Added 2010-02-26
                            noTerminals = Boolean.parseBoolean(row[1]);

                        } else if ("make_time_series".equals(row[0])) {
                            makeTimeSeries = Boolean.parseBoolean(row[1]);

                        } else if ("make_time_series_with_index".equals(row[0])) {
                            makeTimeSeriesWithIndex = Boolean.parseBoolean(row[1]);

                        } else if ("min_nodes".equals(row[0])) {
                            // 2010-03-01: Added this and min_nodes_penalty
                            String[] opt = row[1].split("[\\s,]+");
                            minNodes = Integer.parseInt(opt[0]);

                            if (minNodes > maxNodes) {
                                System.out.println("minNodes (" + minNodes + ") >  maxNodes (" + maxNodes + ") which is weird. Cannot continue. "); 
                                System.exit(1);
                            }
                            minNodesPenalty = Integer.parseInt(opt[1]);

                        } else if ("alldifferent_variables".equals(row[0])) {
                            // added 2010-03-02
                            String[] opt = row[1].split("[\\s,]+");
                            alldifferentVariables = Boolean.parseBoolean(opt[0]);
                            alldifferentVariablesPenalty = Double.parseDouble(opt[1]);
                        // } else if ("make_index_seq".equals(row[0])) {
                        // makeIndexSeq = Boolean.parseBoolean(row[1]);

                        } else {                            
                            System.out.println("Unknown keyword: " + row[0] + " on line " + lineCount);
                            System.exit(1);

                        }
                    }

                } // end if(gotData)

            } // end while

            inr.close();

            // 2010-02-27: if makeTimeSeries is set we
            // makes a timeseries of the first data row.
            // It is undocumented what will happen with more than one
            // data rows.
            //
            // The lag is the number of input variables + 1.
            // 
            if (makeTimeSeries || makeTimeSeriesWithIndex) {

                // last we substitute theData with this
                ArrayList<Double[]> theDataNew = new ArrayList<Double[]>();

                Double[] dat = theData.get(0);
                int numElements = dat.length;
                System.out.println("Making timeseries, #elements: " + numElements);
                int cols = numInputVariables + 1;
                if (makeTimeSeriesWithIndex) {
                    cols++;
                }
                for (int i = 0; i < numElements - cols; i++) {
                    Double[] tmp = new Double[cols];
                    int jStart = 0;
                    if (makeTimeSeriesWithIndex) {
                        System.out.print(i+1 + " ");
                        tmp[0] = (double)i+1;
                        jStart = 1;
                    }
                    for (int j = jStart; j < cols; j++) {
                        System.out.print(dat[i+j] + " ");
                        tmp[j] = dat[i+j];
                    }
                    System.out.println();
                    theDataNew.add(tmp);
                }
                
                theData = theDataNew;
            }


            //
            // Now we know everything to be known.
            // Construct the matrix from the file.
            // -----------------------------------
            int r = theData.size();
            int c = theData.get(0).length;
 
            Double[][] dataTmp = new Double[r][c];

            // TODO: ignore the variables in ignoreVariables
            for(int i = 0; i < r; i++) {
                Double[] this_row = theData.get(i);
                for(int j = 0; j < c; j++) {
                    dataTmp[i][j] = this_row[j];
                }
            }

            // Since we calculate the error on the variable we
            // must transpose the data matrix
            // -----------------------------------------------
            data = transposeMatrix(dataTmp);
            numRows = data[0].length;
            System.out.println("It was " + numRows + " data rows");


            //
            // Do the same thing with the validation data
            //
            // TODO: This should really be a separate method
            if (validationPCT > 0.0d && theValidationSet != null && theValidationSet.size() > 0) {
                int r_v = theValidationSet.size();
                int c_v = theValidationSet.get(0).length;
                Double[][] dataTmp_v = new Double[r_v][c_v];
                
                // TODO: ignore the variables in ignoreVariables
                for(int i = 0; i < r_v; i++) {
                    Double[] this_row = theValidationSet.get(i);
                    for(int j = 0; j < c_v; j++) {
                        dataTmp_v[i][j] = this_row[j];
                    }
                }
                validationSet = transposeMatrix(dataTmp_v);
                System.out.println("It was " + validationSet[0].length  + " data rows in the validation data set");
            }


            //
            // Do the same thing with the test data
            //
            if (theTestData.size() > 0) {
                int r_v = theTestData.size();
                int c_v = theTestData.get(0).length;
                Double[][] dataTmp_v = new Double[r_v][c_v];
                
                // TODO: ignore the variables in ignoreVariables
                for(int i = 0; i < r_v; i++) {
                    Double[] this_row = theTestData.get(i);
                    for(int j = 0; j < c_v; j++) {
                        dataTmp_v[i][j] = this_row[j];
                    }
                }
                // testData = transposeMatrix(dataTmp_v);
                testData = dataTmp_v;
                System.out.println("It was " + testData.length  + " data rows in the user defined data set");
            }


        } catch (IOException e) {
            System.out.println(e);
            System.exit(1);
        }
  
    } // end readFile
    
    
        //
    // Transpose matrix
    // ----------------
    public static Double [][] transposeMatrix(Double [][] m){
        int r = m.length;
        int c = m[0].length;
        Double [][] t = new Double[c][r];
        for(int i = 0; i < r; ++i){
            for(int j = 0; j < c; ++j){
                t[j][i] = m[i][j];
            }
        }
        return t;

    } // end transposeMatrix
    
    
    
    public static void main(String[] args) {
        
    }

    public PGTeste(GPConfiguration a_conf) throws InvalidConfigurationException {
        super(a_conf);
    }

    private CommandGene[] makeCommands(GPConfiguration conf, String[] functions, double lowerRange, double upperRange, String string) {
               ArrayList<CommandGene> commandsList = new ArrayList<CommandGene>();
        int len = functions.length;
 
/*
        try {
            for(int i = 0; i < len; i++) {
/*
                //
                // Note: all functions may not be applicable...
                //
                if ("Multiply".equals(functions[i])) {
                    commandsList.add(new Multiply(conf, CommandGene.DoubleClass));
                    if (isADF && "boolean".equals(adfType)) {
                        commandsList.add(new Multiply(conf, CommandGene.BooleanClass));
                    }

                } else if ("Multiply3".equals(functions[i])) {
                    commandsList.add(new Multiply3(conf, CommandGene.DoubleClass));
                    if (isADF && "boolean".equals(adfType)) {
                        commandsList.add(new Multiply3(conf, CommandGene.BooleanClass));
                    }

                } else if ("Add".equals(functions[i])) {
                    commandsList.add(new Add(conf, CommandGene.DoubleClass));
                    if (isADF && "boolean".equals(adfType)) {
                        commandsList.add(new Add(conf, CommandGene.BooleanClass));
                    }

                } else if ("Divide".equals(functions[i])) {
                    commandsList.add(new Divide(conf, CommandGene.DoubleClass));
                    if (isADF && "boolean".equals(adfType)) {
                        commandsList.add(new Divide(conf, CommandGene.BooleanClass));
                    }

                } else if ("DivideIntD".equals(functions[i])) {
                    commandsList.add(new DivideIntD(conf, CommandGene.DoubleClass));

                } else if ("DivideProtected".equals(functions[i])) {
                    commandsList.add(new DivideProtected(conf, CommandGene.DoubleClass));

                } else if ("Add3".equals(functions[i])) {
                    commandsList.add(new Add3(conf, CommandGene.DoubleClass));
                    if (isADF && "boolean".equals(adfType)) {
                        commandsList.add(new Add3(conf, CommandGene.BooleanClass));
                    }

                } else if ("Add4".equals(functions[i])) {
                    commandsList.add(new Add4(conf, CommandGene.DoubleClass));
                    if (isADF && "boolean".equals(adfType)) {
                        commandsList.add(new Add4(conf, CommandGene.BooleanClass));
                    }

                } else if ("Subtract".equals(functions[i])) {
                    commandsList.add(new Subtract(conf, CommandGene.DoubleClass));
                    if (useADF && "boolean".equals(adfType)) {
                        commandsList.add(new Subtract(conf, CommandGene.BooleanClass));
                    }

                } else if ("Sine".equals(functions[i])) {
                    commandsList.add(new Sine(conf, CommandGene.DoubleClass));

                } else if ("ArcSine".equals(functions[i])) {
                    commandsList.add(new ArcSine(conf, CommandGene.DoubleClass));

                } else if ("Tangent".equals(functions[i])) {
                    commandsList.add(new Tangent(conf, CommandGene.DoubleClass));

                } else if ("ArcTangent".equals(functions[i])) {
                    commandsList.add(new ArcTangent(conf, CommandGene.DoubleClass));

                } else if ("Cosine".equals(functions[i])) {
                    commandsList.add(new Cosine(conf, CommandGene.DoubleClass));

                } else if ("ArcCosine".equals(functions[i])) {
                    commandsList.add(new ArcCosine(conf, CommandGene.DoubleClass));

                } else if ("Exp".equals(functions[i])) {
                    commandsList.add(new Exp(conf, CommandGene.DoubleClass));

                } else if ("Log".equals(functions[i])) {
                    commandsList.add(new Log(conf, CommandGene.DoubleClass));

                } else if ("Abs".equals(functions[i])) {
                    commandsList.add(new Abs(conf, CommandGene.DoubleClass));

                } else if ("Pow".equals(functions[i])) {
                    commandsList.add(new Pow(conf, CommandGene.DoubleClass));

                } else if ("Round".equals(functions[i])) {
                    commandsList.add(new Round(conf, CommandGene.DoubleClass));

                } else if ("RoundD".equals(functions[i])) {
                    commandsList.add(new RoundD(conf, CommandGene.DoubleClass));

                } else if ("Ceil".equals(functions[i])) {
                    commandsList.add(new Ceil(conf, CommandGene.DoubleClass));

                } else if ("Floor".equals(functions[i])) {
                    commandsList.add(new Floor(conf, CommandGene.DoubleClass));

                } else if ("Modulo".equals(functions[i])) {
                    commandsList.add(new Modulo(conf, CommandGene.DoubleClass));
                    if (isADF && "boolean".equals(adfType)) {
                        commandsList.add(new Modulo(conf, CommandGene.BooleanClass));
                    }

                } else if ("ModuloD".equals(functions[i])) {
                    commandsList.add(new ModuloD(conf, CommandGene.DoubleClass));
                    if (isADF && "boolean".equals(adfType)) {
                        commandsList.add(new ModuloD(conf, CommandGene.BooleanClass));
                    }

                } else if ("ModuloReplaceD".equals(functions[i])) {
                    
                    // this is quite experimental
                    commandsList.add(new ModuloReplaceD(conf, CommandGene.DoubleClass, modReplace));
                    if (isADF && "boolean".equals(adfType)) {
                        commandsList.add(new ModuloReplaceD(conf, CommandGene.BooleanClass, modReplace));
                    }

                } else if ("Max".equals(functions[i])) {
                    commandsList.add(new Max(conf, CommandGene.DoubleClass));
                    if (isADF && "boolean".equals(adfType)) {
                        commandsList.add(new Max(conf, CommandGene.BooleanClass));
                    }

                } else if ("Min".equals(functions[i])) {
                    commandsList.add(new Min(conf, CommandGene.DoubleClass));
                    if (isADF && "boolean".equals(adfType)) {
                        commandsList.add(new Min(conf, CommandGene.BooleanClass));
                    }

                } else if ("Sqrt".equals(functions[i])) {
                    // Note: This uses my Sqrt.java file
                    commandsList.add(new Sqrt(conf, CommandGene.DoubleClass));

                } else if ("Square".equals(functions[i])) {
                    // Note: This uses my Square.java file
                    commandsList.add(new Square(conf, CommandGene.DoubleClass));

                } else if ("Cube".equals(functions[i])) {
                    // Note: This uses my Cube.java file
                    commandsList.add(new Cube(conf, CommandGene.DoubleClass));

                } else if ("Logistic".equals(functions[i])) {
                    // Note: This uses my Logistic.java file
                    commandsList.add(new Logistic(conf, CommandGene.DoubleClass));

                } else if ("Gaussian".equals(functions[i])) {
                    // Note: This uses my Gaussian.java file
                    commandsList.add(new Gaussian(conf, CommandGene.DoubleClass));

                } else if ("Sigmoid".equals(functions[i])) {
                    // Note: This uses my Sigmoid.java file
                    commandsList.add(new Sigmoid(conf, CommandGene.DoubleClass));

                } else if ("Gamma".equals(functions[i])) {
                    // Note: This uses my Gamma.java file
                    commandsList.add(new Gamma(conf, CommandGene.DoubleClass));

                } else if ("Step".equals(functions[i])) {
                    // Note: This uses my Step.java file
                    commandsList.add(new Step(conf, CommandGene.DoubleClass));

                } else if ("Sign".equals(functions[i])) {
                    // Note: This uses my Sign.java file
                    commandsList.add(new Sign(conf, CommandGene.DoubleClass));

                } else if ("Hill".equals(functions[i])) {
                    // Note: This uses my Hill.java file
                    commandsList.add(new Hill(conf, CommandGene.DoubleClass));


                } else {
                    System.out.println("Unkown function: " + functions[i]);
                    System.exit(1);

                }
            }
            
            if (!noTerminals) {
                commandsList.add(new Terminal(conf, CommandGene.DoubleClass, lowerRange, upperRange, terminalWholeNumbers));
                // commandsList.add(new Terminal(conf, CommandGene.BooleanClass, lowerRange, upperRange, terminalWholeNumbers));
            }

            // ADF
            // Just add the ADF to the "normal" command list (i.e. not to the ADF list)
            if (useADF && !"ADF".equals(type)) {
                commandsList.add(new ADF(conf, 1, adfArity));
                // for (int i = 0; i <= adfArity; i++) {
                //    commandsList.add(new ADF(conf, i, adfArity));
                // }
            }

            if (constants != null) {
                for (int i = 0; i < constants.size(); i++) {
                    Double constant = constants.get(i);
                    commandsList.add(new Constant(conf, CommandGene.DoubleClass, constant));
                }
            }
        } catch (Exception e) {
            System.out.println(e);

        }

        CommandGene[] commands = new CommandGene[commandsList.size()];
        commandsList.toArray(commands);
        
        return commands;
        */
        return null;
    }
    


}
